<?php
namespace TwinPeaks\Core;

class CSGMLElement
{
	const EL_TYPE_VOID = 1;
	const EL_TYPE_NON_VOID = 2;
    
	/**
	 * @var CDummy
	 */
	static protected $dummyObj;
	
	/**
	 * @var CApplication
	 */
	static protected $appInst;

	/**
	 * @var string
	 */
	static protected $attrValueQuot = '"';

	/**
	 * @var string Element name
	 */
	private $elName;
	
	/**
	 * @var array 
	 */
	private $elAttrs = [];
	
	/**
	 * @var integer Type of SGML element
	 */
	private $elType;
	
	/**
	 * @var array|string Element text or array of nested elements
	 */
	private $contentMixed = '';
	
	/**
	 * @return
	 */
	function __construct($name, $type = null)
	{
		$this->elName = $name;
		if (!isset(self::$dummyObj))
			self::$dummyObj=  new CDummy();
		if (!isset(self::$appInst))
			self::$appInst = CApplication::getMyChild()[0];
	}
	
	/**
	 * @return void
	 */
	final function _print()
	{
		self::$appInst->_echo($this->toString());
	}
    
	/**
	 * @return CSGMLElement|CDummy
	 */
	final function _If($boolValue, $hopsCounter = 1)
	{
		if ($boolValue)
			return $this;
		else
		{
			self::$dummyObj->setHopsCounter($hopsCounter, $this);
			return self::$dummyObj;
		}
	}

	/**
	 * @return CSGMLElement|CDummy
	 */
	final function _IfNot($boolValue, $hopsCounter = 1)
	{
		return $this->_If(!$boolValue, $hopsCounter);
	}

	/**
	 * @return string
	 */
	function getName()
	{
		return $this->elName;
	}
	
	/**
	 * @return string
	 */
	function getStartTag()
	{
		$tagStr = '<' . $this->elName;
		foreach ($this->elAttrs as $attrName => $attrValue)
		{
			$tagStr .= ' ' . $attrName . '=';
			$tagStr .= self::$attrValueQuot .
				static::escapeAttr($attrValue) . self::$attrValueQuot;
		}
		$tagStr .= '>';		
		return $tagStr;
	}
	
	/**
	 * @return string
	 */
	function getEndTag()
	{
		return '</' . $this->elName . '>';
	}
	
	/**
	 * @return string
	 */
	static function escapeAttr($string)
	{
		// Specified options of the function will do:
		// 1. Convert double-quotes and leave single-quotes alone
		// 2. Replace invalid code unit sequences with a Unicode Replacement
		//+ Character U+FFFD (UTF-8) or &#FFFD; (otherwise) instead of returning an empty string
		return htmlspecialchars($string, ENT_COMPAT|ENT_SUBSTITUTE);
	}
	
	/**
	 * @return string
	 */
	static function escapeText($text)
	{
		return $text;
	}

	/**
	 * @return string|array
	 */
	function getContent()
	{
		return $this->contentMixed;
	}

	/**
	 * @return CSGMLElement
	 */
	function setContent($content)
	{
		if ($content instanceof \Closure)
			$this->contentMixed = $content->bindTo($this, get_class($this));
		else
			$this->contentMixed = $content;

		return $this;
	}

	/**
	 * @return string
	 */
	final function toString()
	{
		$elStr = $this->getStartTag();
		
		if ($this->elType == self::EL_TYPE_VOID)
			return $elStr;
		
		if (is_array($this->contentMixed))
			foreach ($this->contentMixed as $nestedEl)
				$elStr .= $nestedEl->toString();
		elseif (is_callable($this->contentMixed))
		{
			ob_start();
			call_user_func($this->contentMixed);
			$elStr .= trim(ob_get_clean());
		}
		else	
			$elStr .= static::escapeText($this->contentMixed);
		
		$elStr .= $this->getEndTag();
		return $elStr;
	}
	
	/**
	 * @return CElement
	 */
	function getAttr($name)
	{
		return $this->elAttrs[$name];
	}
	
	/**
	 * @return CElement
	 */
	function setAttr($name, $value)
	{
		$this->elAttrs[$name] = $value;
		return $this;
	}
	
	/**
	 * @return boolean
	 */
	final function hasAttr($name)
	{
		return isset($this->elAttrs[$name]) ? true : false;
	}
	
	/**
	 * @return CElement
	 */
	function removeAttr($name)
	{
		unset($this->elAttrs[$name]);
		return $this;
	}

	/**
	 * @return string
	 */
	final function __toString()
	{
		return $this->toString();
	}
}
